/**
 * @author gero3 / https://github.com/gero3
 * @author tentone / https://github.com/tentone
 * @author troy351 / https://github.com/troy351
 *
 * Requires opentype.js to be included in the project.
 * Loads TTF files and converts them into typeface JSON that can be used directly
 * to create THREE.Font objects.
 */

import {
	FileLoader,
	Loader
} from "../lib/three.module";
import { opentype } from "../lib/opentype.module.min";

var TTFLoader = function (manager) {

	Loader.call(this, manager);

	this.reversed = false;

};


TTFLoader.prototype = Object.assign(Object.create(Loader.prototype), {

	constructor: TTFLoader,

	load: function (url, onLoad, onProgress, onError) {

		var scope = this;

		var loader = new FileLoader(this.manager);
		loader.setPath(this.path);
		loader.setResponseType('arraybuffer');
		loader.load(url, function (buffer) {

			onLoad(scope.parse(buffer));

		}, onProgress, onError);

	},

	parse: function (arraybuffer) {

		function convert(font, reversed) {

			var round = Math.round;

			var glyphs = {};
			var scale = (100000) / ((font.unitsPerEm || 2048) * 72);

			var glyphIndexMap = font.encoding.cmap.glyphIndexMap;
			var unicodes = Object.keys(glyphIndexMap);

			for (var i = 0; i < unicodes.length; i++) {

				var unicode = unicodes[i];
				var glyph = font.glyphs.glyphs[glyphIndexMap[unicode]];

				if (unicode !== undefined) {

					var token = {
						ha: round(glyph.advanceWidth * scale),
						x_min: round(glyph.xMin * scale),
						x_max: round(glyph.xMax * scale),
						o: ''
					};

					if (reversed) {

						glyph.path.commands = reverseCommands(glyph.path.commands);

					}

					glyph.path.commands.forEach(function (command) {

						if (command.type.toLowerCase() === 'c') {

							command.type = 'b';

						}

						token.o += command.type.toLowerCase() + ' ';

						if (command.x !== undefined && command.y !== undefined) {

							token.o += round(command.x * scale) + ' ' + round(command.y * scale) + ' ';

						}

						if (command.x1 !== undefined && command.y1 !== undefined) {

							token.o += round(command.x1 * scale) + ' ' + round(command.y1 * scale) + ' ';

						}

						if (command.x2 !== undefined && command.y2 !== undefined) {

							token.o += round(command.x2 * scale) + ' ' + round(command.y2 * scale) + ' ';

						}

					});

					glyphs[String.fromCodePoint(glyph.unicode)] = token;

				}

			}

			return {
				glyphs: glyphs,
				familyName: font.getEnglishName('fullName'),
				ascender: round(font.ascender * scale),
				descender: round(font.descender * scale),
				underlinePosition: font.tables.post.underlinePosition,
				underlineThickness: font.tables.post.underlineThickness,
				boundingBox: {
					xMin: font.tables.head.xMin,
					xMax: font.tables.head.xMax,
					yMin: font.tables.head.yMin,
					yMax: font.tables.head.yMax
				},
				resolution: 1000,
				original_font_information: font.tables.name
			};

		}

		function reverseCommands(commands) {

			var paths = [];
			var path;

			commands.forEach(function (c) {

				if (c.type.toLowerCase() === 'm') {

					path = [c];
					paths.push(path);

				} else if (c.type.toLowerCase() !== 'z') {

					path.push(c);

				}

			});

			var reversed = [];

			paths.forEach(function (p) {

				var result = {
					type: 'm',
					x: p[p.length - 1].x,
					y: p[p.length - 1].y
				};

				reversed.push(result);

				for (var i = p.length - 1; i > 0; i--) {

					var command = p[i];
					var result = { type: command.type };

					if (command.x2 !== undefined && command.y2 !== undefined) {

						result.x1 = command.x2;
						result.y1 = command.y2;
						result.x2 = command.x1;
						result.y2 = command.y1;

					} else if (command.x1 !== undefined && command.y1 !== undefined) {

						result.x1 = command.x1;
						result.y1 = command.y1;

					}

					result.x = p[i - 1].x;
					result.y = p[i - 1].y;
					reversed.push(result);

				}

			});

			return reversed;

		}

		if (typeof opentype === 'undefined') {

			console.warn('THREE.TTFLoader: The loader requires opentype.js. Make sure it\'s included before using the loader.');
			return null;

		}

		return convert(opentype.parse(arraybuffer), this.reversed);

	}

});

export { TTFLoader };
